#include "kugou.h"
#include "json.hpp"
#include "base64.h"
#include <chrono>
#include <thread>
#include <iostream>
#include <glib.h>
#include "lyric_log.h"

using json = nlohmann::json;

// 使用 GLib 正则表达式检查是否包含时间戳格式 [mm:ss.xx] 或 [mm:ss.xxx]
static gboolean has_timestamp(const gchar* line) {
    if (!line) {
        return FALSE;
    }
    
    // 使用 GLib 正则表达式匹配时间戳格式
    static GRegex* regex = NULL;
    if (!regex) {
        // 匹配 [mm:ss.xx] 或 [mm:ss.xxx] 格式
        regex = g_regex_new("\\[[0-9]{2}:[0-9]{2}\\.[0-9]{2,3}\\]", G_REGEX_OPTIMIZE, (GRegexMatchFlags)0, NULL);
        if (!regex) {
            lyric_log_error("has_timestamp: failed to compile regex");
            return FALSE;
        }
    }
    
    return g_regex_match(regex, line, (GRegexMatchFlags)0, NULL);
}

// 使用 GLib 字符串分割函数
gchar** str_split(const gchar* s, const gchar* delimiter) {
    if (!s || !delimiter) {
        return NULL;
    }
    return g_strsplit(s, delimiter, -1);
}

gchar* LycrisDetial(const gchar* lycris)
{
    lyric_log_debug("LycrisDetial: starting, input length: %zu", strlen(lycris));
    
    if (!lycris) {
        return NULL;
    }
    
    GString* result_lycris = g_string_new("");
    
    gchar** datas = str_split(lycris, "\n");
    if (!datas) {
        g_string_free(result_lycris, TRUE);
        return NULL;
    }
    
    gint line_count = g_strv_length(datas);
    lyric_log_debug("LycrisDetial: split into %d lines", line_count);
    
    gint matched_count = 0;
    gint processed_count = 0;
    
    lyric_log_debug("LycrisDetial: starting processing loop (using GLib regex)");
    
    // 使用 GLib 正则表达式匹配时间戳
    for (gint i = 0; datas[i] != NULL; i++) {
        processed_count++;
        
        // 每处理10行输出一次进度
        if (processed_count % 10 == 0) {
            lyric_log_debug("LycrisDetial: processed %d/%d lines, matched %d", processed_count, line_count, matched_count);
        }
        
        // 使用 GLib 正则表达式检查时间戳
        if (has_timestamp(datas[i])) {
            g_string_append(result_lycris, datas[i]);
            g_string_append_c(result_lycris, '\n');
            matched_count++;
        }
    }
    
    lyric_log_debug("LycrisDetial: processed all lines, matched %d lines, output length: %zu", matched_count, result_lycris->len);
    
    gchar* result = g_string_free(result_lycris, FALSE);
    g_strfreev(datas);
    
    return result;
}

gchar* urlencode(const gchar* url) {
    if (!url) {
        return NULL;
    }
    
    GString* encoded = g_string_new("");
    for (const gchar* p = url; *p; p++) {
        unsigned char c = (unsigned char)*p;
        // Perform URL encoding based on RFC 3986
        if (g_ascii_isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
            g_string_append_c(encoded, c); // Keep alphanumeric characters, '-', '_', '.', '~'
        } else {
            // Encode other characters as %xx
            g_string_append_printf(encoded, "%%%02X", c);
        }
    }
    return g_string_free(encoded, FALSE);
}


GList* GetFileHashs(const gchar* artist, const gchar* song, struct curl_slist* headers)
{
    GList* songs = NULL;

    gchar* song_artist = g_strconcat(song, "+", artist, NULL);
    gchar* encodeStr1 = urlencode(song_artist);
    gchar* getFileHash = g_strconcat("https://songsearch.kugou.com/song_search_v2?filter=2&platform=WebFilter&keyword=", 
                                    encodeStr1, "&pagesize=10&page=1", NULL);

    gchar* fileHashContent = text_downloader(headers, getFileHash);
    if (!fileHashContent) {
        lyric_log_error("GetFileHashs: failed to download content");
        g_free(song_artist);
        g_free(encodeStr1);
        g_free(getFileHash);
        return NULL;
    }
    
    // 解析 JSON 并检查错误
    json j1;
    try {
        j1 = json::parse(fileHashContent);
    } catch (const json::exception& e) {
        lyric_log_error("GetFileHashs: JSON parse error: %s", e.what());
        lyric_log_debug("GetFileHashs: content: %s", fileHashContent);
        g_free(song_artist);
        g_free(encodeStr1);
        g_free(getFileHash);
        g_free(fileHashContent);
        return NULL;
    }
    
    // 检查 status 字段
    if (j1.find("status") != j1.end()) {
        int status = j1["status"];
        if (status != 1) {
            lyric_log_warning("GetFileHashs: unexpected status value: %d", status);
        }
    }
    
    // 检查 error_code 和 error_msg
    if (j1.find("error_code") != j1.end() && j1.find("error_msg") != j1.end()) {
        int error_code = j1["error_code"];
        std::string error_msg;
        j1.at("error_msg").get_to(error_msg);
        
        // 如果 error_code 不为 0，表示有错误
        if (error_code != 0) {
            lyric_log_error("GetFileHashs: API error - error_code: %d, error_msg: %s", error_code, error_msg.c_str());
            lyric_log_debug("GetFileHashs: artist=%s, song=%s", artist ? artist : "(null)", song ? song : "(null)");
            g_free(song_artist);
            g_free(encodeStr1);
            g_free(getFileHash);
            g_free(fileHashContent);
            return NULL;
        }
    }
    
    // 检查 data 字段是否存在
    if (j1.find("data") == j1.end()) {
        lyric_log_error("GetFileHashs: 'data' field not found in JSON response");
        lyric_log_debug("GetFileHashs: content: %s", fileHashContent);
        g_free(song_artist);
        g_free(encodeStr1);
        g_free(getFileHash);
        g_free(fileHashContent);
        return NULL;
    }
    
    auto j11 = j1.at("data");
    
    // 检查 lists 字段是否存在
    if (j11.find("lists") == j11.end()) {
        lyric_log_warning("GetFileHashs: 'lists' field not found in data, returning empty list");
        g_free(song_artist);
        g_free(encodeStr1);
        g_free(getFileHash);
        g_free(fileHashContent);
        return NULL;
    }
    
    auto j12 = j11.at("lists");
    int size = j12.size();
    if (size > 10) {
        size = 10;
    }
    int i = 0;
    for (json::iterator it = j12.begin(); it != j12.end(); ++it, ++i)
    {
        json j13 = *it;
        std::string fileHash;
        j13.at("FileHash").get_to(fileHash);
        std::string songName;
        j13.at("SongName").get_to(songName);
        std::string albumName;
        j13.at("AlbumName").get_to(albumName);
        std::string singerName;
        j13.at("SingerName").get_to(singerName);
        // std::cout << fileHash << ", " << songName << ", " << albumName << ", " << singerName << std::endl;

        songs = g_list_append(songs, g_strdup(singerName.c_str()));
        songs = g_list_append(songs, g_strdup(songName.c_str()));
        songs = g_list_append(songs, g_strdup(albumName.c_str()));
        songs = g_list_append(songs, g_strdup(fileHash.c_str()));
        if (i >= size) {
            break;
        }

    }

    g_free(song_artist);
    g_free(encodeStr1);
    g_free(getFileHash);
    g_free(fileHashContent);
    return songs;
}


gchar* GetFileHash(const gchar* song, const gchar* artist, struct curl_slist* headers)
{
    gchar* song_artist = g_strconcat(song, "+", artist, NULL);
    gchar* encodeStr1 = urlencode(song_artist);
    gchar* getFileHash = g_strconcat("https://songsearch.kugou.com/song_search_v2?filter=2&platform=WebFilter&keyword=", 
                                    encodeStr1, "&pagesize=10&page=1", NULL);

    gchar* fileHashContent = text_downloader(headers, getFileHash);
    if (!fileHashContent) {
        g_free(song_artist);
        g_free(encodeStr1);
        g_free(getFileHash);
        return NULL;
    }
    
    auto j1 = json::parse(fileHashContent);
    auto j11 = j1.at("data");
    auto j12 = j11.at("lists");
    gchar* fileHash = NULL;
    if (j12.size() > 0) {
        auto j13 = j12[0];
        std::string fileHashStr;
        j13.at("FileHash").get_to(fileHashStr);
        fileHash = g_strdup(fileHashStr.c_str());
    }
    lyric_log_debug("FileHash: %s", fileHash ? fileHash : "NULL");
    
    g_free(song_artist);
    g_free(encodeStr1);
    g_free(getFileHash);
    g_free(fileHashContent);
    return fileHash;
}

gboolean GetAccesskey(const gchar* fileHash, struct curl_slist* headers, gchar** id, gchar** accesskey)
{
    gchar* getAccesskey = g_strconcat("http://lyrics.kugou.com/search?ver=1&man=yes&client=mobi&hash=", fileHash, NULL);

    gchar* accesskeyContent = text_downloader(headers, getAccesskey);
    if (!accesskeyContent || strlen(accesskeyContent) == 0) {
        g_free(getAccesskey);
        g_free(accesskeyContent);
        return FALSE;
    }
    
    auto j2 = json::parse(accesskeyContent);
    if (j2.contains("candidates") && j2["candidates"].size() > 0) {
        auto j21 = j2["candidates"][0];
        if (j21.find("id") != j21.end()) {
            std::string idStr;
            j21.at("id").get_to(idStr);
            *id = g_strdup(idStr.c_str());
        }
        if (j21.find("accesskey") != j21.end()) {
            std::string accesskeyStr;
            j21.at("accesskey").get_to(accesskeyStr);
            *accesskey = g_strdup(accesskeyStr.c_str());
        }
        lyric_log_debug("GetAccesskey：id: %s, accesskey: %s", *id ? *id : "NULL", *accesskey ? *accesskey : "NULL");
        g_free(getAccesskey);
        g_free(accesskeyContent);
        return TRUE;
    }
    
    g_free(getAccesskey);
    g_free(accesskeyContent);
    return FALSE;
}

gchar* GetLyrics(const gchar* id, const gchar* accesskey, struct curl_slist* headers)
{
    gchar* getLyrics = g_strconcat("http://lyrics.kugou.com/download?ver=1&client=iphone&fmt=lrc&charset=utf8&id=", 
                                  id, "&accesskey=", accesskey, NULL);

    lyric_log_debug("GetLyrics: downloading from URL");
    gchar* result = text_downloader(headers, getLyrics);
    lyric_log_debug("GetLyrics: downloaded, result length: %zu", strlen(result));
    
    if (!result || strlen(result) == 0) {
        lyric_log_error("GetLyrics: empty result");
        g_free(getLyrics);
        g_free(result);
        return NULL;
    }
    
    try {
        auto js = json::parse(result);
        std::string content;
        if (js.find("content") != js.end()) {
            js.at("content").get_to(content);
            lyric_log_debug("GetLyrics: content field found, length: %zu", content.length());
        } else {
            lyric_log_error("GetLyrics: 'content' field not found in JSON");
            g_free(getLyrics);
            g_free(result);
            return NULL;
        }
        
        std::string data = base64_decode(content);
        lyric_log_debug("GetLyrics: base64 decoded, data length: %zu", data.length());
        
        lyric_log_debug("GetLyrics: calling LycrisDetial...");
        gchar* lyrics = LycrisDetial(data.c_str());
        lyric_log_debug("GetLyrics: LycrisDetial returned, length: %zu", lyrics ? strlen(lyrics) : 0);
        if (lyrics && strlen(lyrics) > 0) {
            gchar* preview = g_strndup(lyrics, MIN(200, strlen(lyrics)));
            lyric_log_debug("GetLyrics: lyrics preview (first 200 chars): %s", preview);
            g_free(preview);
        }
        
        g_free(getLyrics);
        g_free(result);
        return lyrics;
    } catch (const json::exception& e) {
        lyric_log_error("GetLyrics: JSON parse error: %s", e.what());
        g_free(getLyrics);
        g_free(result);
        return NULL;
    } catch (const std::exception& e) {
        lyric_log_error("GetLyrics: std exception: %s", e.what());
        g_free(getLyrics);
        g_free(result);
        return NULL;
    } catch (...) {
        lyric_log_error("GetLyrics: unknown exception");
        g_free(getLyrics);
        g_free(result);
        return NULL;
    }
}

gchar* down_lyrics(const gchar* artist, const gchar* song)
{
    lyric_log_debug("down_lyrics: starting for artist=%s, song=%s", artist, song);
    struct curl_slist* headers = nullptr;
    headers = curl_slist_append(headers, "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:76.0)");
    headers = curl_slist_append(headers, "Content-Type:application/x-www-form-urlencoded; charset=UTF-8");
    headers = curl_slist_append(headers, "Accept:application/json, text/javascript, */*; q=0.01");
    headers = curl_slist_append(headers, "Accept-Language:zh-CN,zh;q=0.8");
    gchar* lyrics = NULL;
    
    lyric_log_debug("down_lyrics: calling GetFileHashs...");
    GList* songs = GetFileHashs(artist, song, headers);
    gint song_count = g_list_length(songs);
    lyric_log_debug("down_lyrics: GetFileHashs returned, size=%d", song_count);
    
    if (song_count > 3) {
        // 获取第4个元素（索引3）作为fileHash
        GList* fileHash_node = g_list_nth(songs, 3);
        if (fileHash_node && fileHash_node->data) {
            gchar* fileHash = (gchar*)fileHash_node->data;
            lyric_log_debug("down_lyrics: fileHash=%s", fileHash);
            std::this_thread::sleep_for(std::chrono::milliseconds(500));
            
            gchar* id = NULL;
            gchar* accesskey = NULL;
            lyric_log_debug("down_lyrics: calling GetAccesskey...");
            if (GetAccesskey(fileHash, headers, &id, &accesskey)) {
                lyric_log_debug("down_lyrics: GetAccesskey succeeded, id=%s", id);
                std::this_thread::sleep_for(std::chrono::milliseconds(600));
                lyric_log_debug("down_lyrics: calling GetLyrics...");
                lyrics = GetLyrics(id, accesskey, headers);
                lyric_log_debug("down_lyrics: GetLyrics returned, length=%zu", lyrics ? strlen(lyrics) : 0);
                
                g_free(id);
                g_free(accesskey);
            } else {
                lyric_log_warning("down_lyrics: GetAccesskey failed");
            }
        }
    } else {
        lyric_log_warning("down_lyrics: GetFileHashs returned insufficient results (size=%d)", song_count);
    }
    
    // 清理songs列表
    if (songs) {
        for (GList* l = songs; l; l = l->next) {
            g_free(l->data);
        }
        g_list_free(songs);
    }
    
    lyric_log_debug("down_lyrics: returning, lyrics length=%zu", lyrics ? strlen(lyrics) : 0);
    return lyrics;
}

gchar* kugou_down_lyrics(const gchar* artist, const gchar* song)
{
	return down_lyrics(artist, song);
}

GList* kugou_get_songs(const gchar* song, const gchar* artist)
{
    struct curl_slist* headers = NULL;
    headers = curl_slist_append(headers, "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:76.0)");
    headers = curl_slist_append(headers, "Content-Type:application/x-www-form-urlencoded; charset=UTF-8");
    headers = curl_slist_append(headers, "Accept:application/json, text/javascript, */*; q=0.01");
    headers = curl_slist_append(headers, "Accept-Language:zh-CN,zh;q=0.8");

    return GetFileHashs(artist, song, headers);
}

struct parsed_lyrics kugou(const gchar* artist, const gchar* song)
{
    struct parsed_lyrics string_lyrics = {NULL, TRUE};

    gchar* lyrics = down_lyrics(artist, song);
    string_lyrics.lyrics = lyrics;

    return string_lyrics;
}

struct parsed_lyrics kugou_lyrics_downloader(const gchar* fileHash)
{
    struct parsed_lyrics string_lyrics = {NULL, FALSE};

    struct curl_slist* headers = nullptr;
    headers = curl_slist_append(headers, "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:76.0)");
    headers = curl_slist_append(headers, "Content-Type:application/x-www-form-urlencoded; charset=UTF-8");
    headers = curl_slist_append(headers, "Accept:application/json, text/javascript, */*; q=0.01");
    headers = curl_slist_append(headers, "Accept-Language:zh-CN,zh;q=0.8");
    
    gchar* id = NULL;
    gchar* accesskey = NULL;
    if (GetAccesskey(fileHash, headers, &id, &accesskey)) {
        std::this_thread::sleep_for(std::chrono::milliseconds(600));
        gchar* lyrics = GetLyrics(id, accesskey, headers);
        string_lyrics.lyrics = lyrics;
        
        g_free(id);
        g_free(accesskey);
    }
    return string_lyrics;
}